﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using DiscoveryLogic.Analytics.DataObjects.Filters;
using DiscoveryLogic.Analytics.DataObjects.Networks;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Common.Utilities;

namespace DiscoveryLogic.Analytics.DataObjects
{
    public class Graph
    {
        #region props
        private readonly Network _Network;
        public Network Network
        {
            get
            {
                return _Network;
            }
        }

        private Dictionary<Guid, Vertice> _Vertices;
        /// <summary></summary>
        public Dictionary<Guid, Vertice> Vertices
        {
            get
            {
                return this._Vertices;
            }
            //set
            //{
            //    this._Vertices = value;
            //}
        }

        private Dictionary<Guid, Edge> _Edges;
        /// <summary></summary>
        public Dictionary<Guid, Edge> Edges
        {
            get
            {
                return this._Edges;
            }
            //set
            //{
            //    this._Edges = value;
            //}
        }

        private int _ExpandLevel=0;
        /// <summary></summary>
        public int ExpandLevel
        {
            get
            {
                return this._ExpandLevel;
            }
            set
            {
                this._ExpandLevel = value;
            }
        }

        private NetworkVisual _NetVisual = NetworkVisual.Default;
        /// <summary></summary>
        public NetworkVisual NetVisual
        {
            get
            {
                return this._NetVisual;
            }
            set
            {
                this._NetVisual = value;
            }
        }

        private NetworkExclusionSetting _ExSetting = NetworkExclusionSetting.Default;
        /// <summary></summary>
        public NetworkExclusionSetting ExSetting
        {
            get
            {
                return this._ExSetting;
            }
            set
            {
                this._ExSetting = value;
                this.ShowAllEntityTypes();
                if(this._ExSetting.ExcludeEntityTypes !=null && this.ExSetting.ExcludeEntityTypes.Count>0)
                {
                    foreach(EntityType excludedEntityType in this._ExSetting.ExcludeEntityTypes)
                    {
                        this.RemoveEntityType(excludedEntityType);
                    }
                }
            }
        }

        private NetworkInclusionSetting _InSetting = NetworkInclusionSetting.Default;
        /// <summary></summary>
        public NetworkInclusionSetting InSetting
        {
            get
            {
                return this._InSetting;
            }
            set
            {
                this._InSetting = value;
            }
        }
        #endregion

        #region ctor
        public Graph(Network network)
        {
            this._Network = network;
            // Dictionary<int, NetworkCluster> clusters = NetworkCluster.Read(this._Network.NetworkName);
            this._Vertices = new Dictionary<Guid, Vertice>();
            Dictionary<EntityType, Dictionary<string, Guid>> entityVerticeMappings =
                new Dictionary<EntityType, Dictionary<string, Guid>>();
            Dictionary<EntityType, Dictionary<string, Guid>> entityVerticeMappings2=
                new Dictionary<EntityType, Dictionary<string, Guid>>();
            //if(clusters !=null && clusters.Count>0)
            //{
            //    foreach(int clusterID in clusters.Keys)
            //    {
            //        NetworkCluster cluster = clusters[clusterID];
            //        if (cluster.ParentClusterID <= this._ExpandLevel)
            //        {
            //            Vertice clusterV = new Vertice(Guid.NewGuid(), cluster.ClusterName, cluster);
            //            this._Vertices.Add(clusterV.ID, clusterV);
            //            if (entityVerticeMappings2.ContainsKey(cluster.EntityType))
            //            {
            //                Dictionary<string, Guid> keyVidMappings = entityVerticeMappings2[cluster.EntityType];
            //                keyVidMappings.Add(clusterID.ToString(), clusterV.ID);
            //                entityVerticeMappings2[cluster.EntityType] = keyVidMappings;
            //            }
            //            else
            //            {
            //                Dictionary<string, Guid> keyVidMappings = new Dictionary<string, Guid>();
            //                keyVidMappings.Add(clusterID.ToString(), clusterV.ID);
            //                entityVerticeMappings2.Add(cluster.EntityType, keyVidMappings);
            //            }

            //            Dictionary<EntityType, List<string>> clusterEntities = cluster.GetAllNestedEntityKeys();
            //            if (clusterEntities != null && clusterEntities.Count > 0)
            //            {
            //                foreach(EntityType et in clusterEntities.Keys)
            //                {
            //                    foreach(string key in clusterEntities[et])
            //                    {
            //                        if (entityVerticeMappings.ContainsKey(et))
            //                        {
            //                            Dictionary<string, Guid> keyVidMappings = entityVerticeMappings[et];
            //                            if (!keyVidMappings.ContainsKey(key))
            //                                keyVidMappings.Add(key, clusterV.ID);
            //                        }
            //                        else
            //                        {
            //                            Dictionary<string, Guid> keyVIDMappings = new Dictionary<string, Guid>();
            //                            keyVIDMappings.Add(key, clusterV.ID);
            //                            entityVerticeMappings.Add(et, keyVIDMappings);
            //                        }
            //                    }
            //                }
            //                foreach (EntityBase entity in cluster.ChildEntities)
            //                {
            //                    Vertice entityV = new Vertice(Guid.NewGuid(), entity.Name, entity);
            //                    this._Vertices.Add(entityV.ID, entityV);
            //                }
            //            }
            //        }
                    
            //    }
            //}
            //else
            {
                if (network.Records != null && network.Records.Count > 0)
                {
                    foreach (EntityBase entity in network.Records)
                    {
                        Vertice v = new Vertice(Guid.NewGuid(), entity);
                        this._Vertices.Add(v.ID, v);
                        if (entityVerticeMappings.ContainsKey(entity.EntityType))
                        {
                            Dictionary<string, Guid> keyVidMappings = entityVerticeMappings[entity.EntityType];
                            if (!keyVidMappings.ContainsKey(entity.PKValue()))
                                keyVidMappings.Add(entity.PKValue(), v.ID);
                        }
                        else
                        {
                            Dictionary<string, Guid> keyVIDMappings = new Dictionary<string, Guid>();
                            keyVIDMappings.Add(entity.PKValue(), v.ID);
                            entityVerticeMappings.Add(entity.EntityType, keyVIDMappings);
                        }
                    }
                }
            }
            this._Edges = new Dictionary<Guid, Edge>();
            if (network.Links != null && network.Links.Count > 0)
            {
                foreach (Link link in network.Links)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    if (entityVerticeMappings.ContainsKey(fromType) &&
                        entityVerticeMappings[fromType].ContainsKey(link.FromKey))
                        fromVID = entityVerticeMappings[fromType][link.FromKey];
                    if(fromVID==Guid.Empty)
                    {
                        if (entityVerticeMappings2.ContainsKey(fromType) &&
                            entityVerticeMappings2[fromType].ContainsKey(link.FromKey))
                            fromVID = entityVerticeMappings2[fromType][link.FromKey];
                    }
                    if (entityVerticeMappings.ContainsKey(toType) &&
                        entityVerticeMappings[toType].ContainsKey(link.ToKey))
                        toVID = entityVerticeMappings[toType][link.ToKey];
                    if(toVID==Guid.Empty)
                    {
                        if (entityVerticeMappings2.ContainsKey(toType) &&
                            entityVerticeMappings2[toType].ContainsKey(link.ToKey))
                            toVID = entityVerticeMappings2[toType][link.ToKey];
                    }

                    if (fromVID != Guid.Empty && toVID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
            }
        }

        public Graph(Network network, NetworkVisual netVisual, 
            NetworkInclusionSetting inclusionSetting, 
            NetworkExclusionSetting exclusionSetting)
            :this(network)
        {
            this._NetVisual = netVisual;
            this._InSetting = inclusionSetting;
            this._ExSetting = exclusionSetting;
        }

        public Graph(List<EntityBase> entityList, List<Link> linkList)
        {
            Network network = new Network("", entityList, linkList);
            // Dictionary<EntityType, Dictionary<string, EntityBase>> entities = network.GetEntities();
            Dictionary<Guid, Vertice> vertices=new Dictionary<Guid, Vertice>();
            Dictionary<Guid, Edge> edges=new Dictionary<Guid, Edge>();
            Dictionary<EntityType, Dictionary<string, Guid>> entityVerticeMappings = new Dictionary<EntityType, Dictionary<string, Guid>>();
            foreach (EntityBase entity in network.Records)
            {
                Guid guid = Guid.NewGuid();
                Vertice v = new Vertice(guid, entity);
                vertices.Add(guid, v);
                if(entityVerticeMappings.ContainsKey(entity.EntityType))
                {
                    Dictionary<string, Guid> idMapping = entityVerticeMappings[entity.EntityType];
                    idMapping.Add(entity.PKValue(), guid);
                    entityVerticeMappings[entity.EntityType] = idMapping;
                }
                else
                {
                    Dictionary<string,Guid> idMappings=new Dictionary<string, Guid>();
                    idMappings.Add(entity.PKValue(), guid);
                    entityVerticeMappings.Add(entity.EntityType, idMappings);
                }
            }
            if (network.Links != null && network.Links.Count > 0)
            {
                foreach (Link link in network.Links)
                {
                    Guid fromID = Guid.Empty;
                    Guid toID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    if (entityVerticeMappings.ContainsKey(fromType) &&
                        entityVerticeMappings[fromType].ContainsKey(link.FromKey))
                        fromID = entityVerticeMappings[fromType][link.FromKey];
                    if (entityVerticeMappings.ContainsKey(toType) &&
                        entityVerticeMappings[toType].ContainsKey(link.ToKey))
                        toID = entityVerticeMappings[toType][link.ToKey];

                    if (fromID != Guid.Empty && toID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(), link.Distance,
                                fromID, toID, link);
                            edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge arc = new Edge(Guid.NewGuid(), link.RelationType.ToString(), link.Distance,
                               fromID, toID, link);
                            edges.Add(arc.ID, arc);
                        }
                    }
                }
            }

            this._Vertices = vertices;
            this._Edges = edges;
        }

        #endregion

        #region filter
        public void Filter(double maxDistance)
        {
            this._Edges = new Dictionary<Guid, Edge>();
            if (this._Network.Links != null && this._Network.Links.Count > 0)
            {
                foreach (Link link in this._Network.Links)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty && 
                        link.Distance<=maxDistance)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
            }
        }

        public void Filter(EntityBase anchor, int maxHop)
        {
            Dictionary<EntityBase, int> entitiesWithinReach = new Dictionary<EntityBase, int>();
            this.FindReachableEntities(anchor,0, maxHop, ref entitiesWithinReach);
            this._Vertices=new Dictionary<Guid, Vertice>();
            if (entitiesWithinReach.Count>0)
            {
                foreach (EntityBase entity in entitiesWithinReach.Keys)
                {
                    Vertice v = new Vertice(Guid.NewGuid(), entity);
                    this._Vertices.Add(v.ID, v);
                }
            }
            this._Edges = new Dictionary<Guid, Edge>();
            if (this._Network.Links != null && this._Network.Links.Count > 0)
            {
                foreach (Link link in this._Network.Links)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
            }
        }

        public void FilterTimeSpan(int year)
        {
            int retention = int.Parse(ConfigurationManager.AppSettings.Get("PubDateRetentionInYears"));
            Range<DateTime> timeFilter = new Range<DateTime>(new DateTime(year, 1, 1), new DateTime(year, 12, 31));
            this._Edges = new Dictionary<Guid, Edge>();
            if (this._Network.Links != null && this._Network.Links.Count > 0)
            {
                foreach (Link link in this._Network.Links)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty &&
                        link.Filter(timeFilter, retention))
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
            }
        }

        private void FindReachableEntities(EntityBase anchor, int currentHopStep, int maxHop, ref Dictionary<EntityBase, int> processedEntities)
        {
            if(currentHopStep >=maxHop)
                return;
            Type anchorType = anchor.GetType();
            foreach(Link link in this._Network.Links)
            {
                if(link.FromType==anchorType && anchor.PKValue()==link.FromKey)
                {
                    EntityBase toEntity=this.Find(link.ToType,link.ToKey);
                    if(toEntity !=null)
                    {
                        if(processedEntities.ContainsKey(toEntity) && processedEntities[toEntity]>currentHopStep +1)
                        {
                            processedEntities[toEntity] = currentHopStep + 1;
                            this.FindReachableEntities(toEntity, currentHopStep+1, maxHop, ref processedEntities);
                        }
                        else if(!processedEntities.ContainsKey(toEntity))
                        {
                            processedEntities.Add(toEntity, currentHopStep + 1);
                            this.FindReachableEntities(toEntity, currentHopStep+1, maxHop, ref processedEntities);
                        }
                    }
                }
            }
        }

        private EntityBase Find(Type type, string key)
        {
            foreach(EntityBase entity in this._Network.Records)
            {
                if(entity.GetType()==type && entity.PKValue()==key)
                    return entity;
            }
            return null;
        }
        #endregion

        #region select entity type
        public void RemoveEntityType(EntityType entityType)
        {
            List<Link> links = this._Network.Links;
            int linksRemoved = this.HideLinks(entityType, links);
            while (linksRemoved > 0)
            {
                links = new List<Link>();
                foreach (Edge edge in this._Edges.Values)
                {
                    links.Add(edge.Tag);
                }
                linksRemoved = this.HideLinks(entityType, links);
            }
        }

        private List<Link> MergeLinks(EntityType entityType, List<Link> links)
        {
            Link[] allLinks = new Link[links.Count];
            links.CopyTo(allLinks, 0);
            List<Link> newLinks = new List<Link>();
            Type type = EntityTypeUtil.GetType(entityType);
            while(true)
            {
                int entityRemoved = 0;
                for(int i=0;i<allLinks.Length;i++)
                {
                    Link link = allLinks[i];
                    if (link.FromType == type && link.ToType == type)
                    {
                        string key1 = link.FromKey;
                        string key2 = link.ToKey;
                        foreach (Link link2 in allLinks)
                        {
                            if (link2 != link)
                            {
                                if (link2.FromType == type &&
                                    link2.FromKey == key1 && link2.ToKey !=key2)
                                {
                                    link2.FromKey = key2;
                                    entityRemoved++;
                                }
                                else if(link2.ToType==type && 
                                    link2.ToKey==key1 && link2.FromKey != key2)
                                {
                                    link2.ToKey = key2;
                                    entityRemoved++;
                                }
                            }
                        }
                        link = null;
                    }
                }
                if(entityRemoved==0)
                    break;
            }

            
            foreach (Link link in allLinks)
                if (link != null)
                    newLinks.Add(link);
            return newLinks;
        }

        private int HideLinks(EntityType entityType, List<Link> links)
        {
            links = this.MergeLinks(entityType, links);
            this._Edges = new Dictionary<Guid, Edge>();

            Type type = EntityTypeUtil.GetType(entityType);
            List<Link> linksToHide = new List<Link>();
            List<Link> linksToAdd = new List<Link>();
            while (true)
            {
                int linksUpdated = 0;
                foreach (Link link1 in links)
                {
                    if (linksToHide.Contains(link1))
                        continue;

                    List<Link> addedHiddenLinks = new List<Link>();
                    if (link1.FromType == type || link1.ToType == type)
                    {
                        EntityBase entityToRemove = null;
                        if (link1.FromType == type)
                            entityToRemove = this.Find(type, link1.FromKey);
                        else
                            entityToRemove = this.Find(type, link1.ToKey);

                        List<EntityBase> toEntities = new List<EntityBase>();
                        foreach (Link link2 in links)
                        {
                            //if (linksToHide.Contains(link2))
                            //    continue;
                            if (link2.FromType == entityToRemove.GetType() &&
                                link2.FromKey == entityToRemove.PKValue())
                            {
                                EntityBase toEntity = this.Find(link2.ToType, link2.ToKey);
                                if (toEntity == null)
                                    continue;
                                toEntities.Add(toEntity);
                                if (!addedHiddenLinks.Contains(link2))
                                {
                                    addedHiddenLinks.Add(link2);
                                    linksUpdated++;
                                }
                            }
                        }
                        List<EntityBase> fromEntities = new List<EntityBase>();
                        foreach (Link link3 in links)
                        {
                            //if (linksToHide.Contains(link3))
                            //    continue;
                            if (link3.ToType == entityToRemove.GetType() &&
                                link3.ToKey == entityToRemove.PKValue())
                            {
                                EntityBase fromEntity = this.Find(link3.FromType, link3.FromKey);
                                if (fromEntity == null)
                                    continue;
                                fromEntities.Add(fromEntity);
                                if (!addedHiddenLinks.Contains(link3))
                                {
                                    addedHiddenLinks.Add(link3);
                                    linksUpdated++;
                                }
                            }
                        }
                        if (addedHiddenLinks.Count == 0)
                            continue;
                        linksToHide.AddRange(addedHiddenLinks);

                        if (fromEntities.Count > 0 && toEntities.Count > 0)
                        {
                            foreach (EntityBase fromEntity in fromEntities)
                            {
                                foreach (EntityBase toEntity in toEntities)
                                {
                                    //foreach (Link linkToAdd in linksToAdd)
                                    //{
                                    //    if (linkToAdd.FromType == fromEntity.GetType() &&
                                    //        linkToAdd.FromKey == fromEntity.PKValue() &&
                                    //        linkToAdd.ToType == toEntity.GetType() &&
                                    //        linkToAdd.ToKey == toEntity.PKValue())
                                    //        break;
                                    //    if (linkToAdd.FromType == toEntity.GetType() &&
                                    //        linkToAdd.FromKey == toEntity.PKValue() &&
                                    //        linkToAdd.ToType == fromEntity.GetType() &&
                                    //        linkToAdd.ToKey == fromEntity.PKValue())
                                    //        break;
                                    //}
                                    List<Link> hiddenLinks = new List<Link>();
                                    bool directional = true;
                                    double distance = 0;
                                    DateTime timeStamp1 = TimeUtil.DbMinTime;
                                    DateTime timeStamp2 = TimeUtil.DbMaxTime;
                                    foreach (Link hiddenLink in addedHiddenLinks)
                                    {
                                        if (hiddenLink.FromType == fromEntity.GetType() && hiddenLink.FromKey == fromEntity.PKValue())
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1 == TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2 == TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                        else if (hiddenLink.ToType == toEntity.GetType() && hiddenLink.ToKey == toEntity.PKValue())
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1 == TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2 == TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                    }

                                    if (timeStamp1 <= timeStamp2)
                                    {
                                        Link newLink = new Link(
                                            fromEntity.GetType(), toEntity.GetType(),
                                            fromEntity.PKValue(), toEntity.PKValue(), 
                                            distance,1, LinkType.CompositeLink,
                                            directional);
                                        if (!this._Network.ContainsLink(newLink))
                                        {
                                            newLink.LinkTimeSpan = new Range<DateTime>(timeStamp1, timeStamp2);
                                            // newLink.HiddenLinks = hiddenLinks;
                                            linksToAdd.Add(newLink);
                                        }
                                    }
                                }
                            }
                            break;
                        }
                        else if (fromEntities.Count > 1)
                        {
                            for (int i = 0; i < fromEntities.Count; i++)
                            {
                                EntityBase entity1 = fromEntities[i];
                                string key1 = entity1.PKValue();
                                for (int k = i + 1; k < fromEntities.Count; k++)
                                {
                                    EntityBase entity2 = fromEntities[k];
                                    string key2 = entity2.PKValue();
                                    bool alreadyAdded = false;
                                    foreach (Link linkToAdd in linksToAdd)
                                    {
                                        if (linkToAdd.FromType == entity1.GetType() &&
                                            linkToAdd.FromKey == key1 &&
                                            linkToAdd.ToType == entity2.GetType() &&
                                            linkToAdd.ToKey == key2)
                                        {
                                            alreadyAdded = true;
                                            break;
                                        }
                                        if (linkToAdd.FromType == entity2.GetType() &&
                                            linkToAdd.FromKey == key2 &&
                                            linkToAdd.ToType == entity1.GetType() &&
                                            linkToAdd.ToKey == key1)
                                        {
                                            alreadyAdded = true;
                                            break;
                                        }
                                    }
                                    if (alreadyAdded)
                                        continue;
                                    List<Link> hiddenLinks = new List<Link>();
                                    bool directional = true;
                                    double distance = 0;

                                    DateTime timeStamp1 = TimeUtil.DbMinTime;
                                    DateTime timeStamp2 = TimeUtil.DbMaxTime;
                                    foreach (Link hiddenLink in addedHiddenLinks)
                                    {
                                        if (hiddenLink.FromType == entity1.GetType() &&
                                            hiddenLink.FromKey == key1)
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1==TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2==TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                        else if (hiddenLink.FromType == entity2.GetType() &&
                                            hiddenLink.FromKey == key2)
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1 == TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2 == TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                    }

                                    if (timeStamp1 <= timeStamp2)
                                    {
                                        Link newLink = new Link(
                                            entity1.GetType(), entity2.GetType(),
                                            entity1.PKValue(), entity2.PKValue(), 
                                            distance, 1, LinkType.CompositeLink,
                                            directional);
                                        if (!this._Network.ContainsLink(newLink))
                                        {
                                            newLink.LinkTimeSpan = new Range<DateTime>(timeStamp1, timeStamp2);
                                            //newLink.HiddenLinks = hiddenLinks;
                                            linksToAdd.Add(newLink);
                                        }
                                    }
                                }
                            }
                        }
                        else if (toEntities.Count > 1)
                        {
                            for (int i = 0; i < toEntities.Count; i++)
                            {
                                EntityBase entity1 = toEntities[i];
                                string key1 = entity1.PKValue();
                                for (int k = i + 1; k < toEntities.Count; k++)
                                {
                                    EntityBase entity2 = toEntities[k];
                                    string key2 = entity2.PKValue();
                                    bool alreadyAdded = false;
                                    foreach (Link linkToAdd in linksToAdd)
                                    {
                                        if (linkToAdd.FromType == entity1.GetType() &&
                                            linkToAdd.FromKey == key1 &&
                                            linkToAdd.ToType == entity2.GetType() &&
                                            linkToAdd.ToKey == key2)
                                        {
                                            alreadyAdded = true;
                                            break;
                                        }
                                        if (linkToAdd.FromType == entity2.GetType() &&
                                            linkToAdd.FromKey == key2 &&
                                            linkToAdd.ToType == entity1.GetType() &&
                                            linkToAdd.ToKey == key1)
                                        {
                                            alreadyAdded = true;
                                            break;
                                        }
                                    }
                                    if (alreadyAdded)
                                        continue;

                                    List<Link> hiddenLinks = new List<Link>();
                                    bool directional = true;
                                    double distance = 0;
                                    DateTime timeStamp1 = TimeUtil.DbMinTime;
                                    DateTime timeStamp2 = TimeUtil.DbMaxTime;
                                    foreach (Link hiddenLink in addedHiddenLinks)
                                    {
                                        if (hiddenLink.ToType == entity1.GetType() &&
                                            hiddenLink.ToKey == key1)
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1 == TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2 == TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                        else if (hiddenLink.ToType == entity2.GetType() &&
                                            hiddenLink.ToKey == key2)
                                        {
                                            hiddenLinks.Add(hiddenLink);
                                            distance += hiddenLink.Distance;
                                            if (!hiddenLink.Directional)
                                                directional = false;
                                            if (timeStamp1 == TimeUtil.DbMinTime || hiddenLink.LinkTimeSpan.FromValue < timeStamp1)
                                                timeStamp1 = hiddenLink.LinkTimeSpan.FromValue;
                                            if (timeStamp2 == TimeUtil.DbMaxTime || hiddenLink.LinkTimeSpan.ToValue > timeStamp2)
                                                timeStamp2 = hiddenLink.LinkTimeSpan.ToValue;
                                        }
                                    }

                                    if (timeStamp1 <= timeStamp2)
                                    {
                                        Link newLink = new Link(
                                            entity1.GetType(), entity2.GetType(),
                                            entity1.PKValue(), entity2.PKValue(), 
                                            distance,1, LinkType.CompositeLink,
                                            directional);
                                        if (!this._Network.ContainsLink(newLink))
                                        {
                                            newLink.LinkTimeSpan = new Range<DateTime>(timeStamp1, timeStamp2);
                                            //newLink.HiddenLinks = hiddenLinks;
                                            linksToAdd.Add(newLink);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                if (linksUpdated == 0)
                    break;
            }

            if (linksToHide.Count == 0 || linksToAdd.Count == 0)
            {
                foreach (Link link in links)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
                return 0;
            }
            else
            {
                foreach (Link link in links)
                {
                    if (linksToHide.Contains(link))
                        continue;
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
                foreach (Link link in linksToAdd)
                {
                    Guid fromVID = Guid.Empty;
                    Guid toVID = Guid.Empty;
                    EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                    EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                    foreach (Vertice v in this._Vertices.Values)
                    {
                        if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                            fromVID = v.ID;
                        if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                            toVID = v.ID;
                    }
                    if (fromVID != Guid.Empty && toVID != Guid.Empty)
                    {
                        if (link.Directional)
                        {
                            Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                                link.Distance, fromVID, toVID, link);
                            this._Edges.Add(arc.ID, arc);
                        }
                        else
                        {
                            Edge e = new Edge(
                                Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                            this._Edges.Add(e.ID, e);
                        }
                    }
                }
                return linksToHide.Count;
            }
            
        }

        public void ShowAllEntityTypes()
        {
            this._Edges=new Dictionary<Guid, Edge>();
            foreach (Link link in this._Network.Links)
            {
                Guid fromVID = Guid.Empty;
                Guid toVID = Guid.Empty;
                EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                foreach (Vertice v in this._Vertices.Values)
                {
                    if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                        fromVID = v.ID;
                    if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                        toVID = v.ID;
                }
                if (fromVID != Guid.Empty && toVID != Guid.Empty)
                {
                    if (link.Directional)
                    {
                        Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                            link.Distance, fromVID, toVID, link);
                        this._Edges.Add(arc.ID, arc);
                    }
                    else
                    {
                        Edge e = new Edge(
                            Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                        this._Edges.Add(e.ID, e);
                    }
                }

            }
        }

        public void RemoveAllEntityTypesExcept(
            EntityType entityType, int maxHop, 
            List<EntityType> aggregateByEntityTypes,
            Dictionary<EntityType,int> maxHopsByEntityTypes)
        {
            Dictionary<Guid, Vertice> vs=new Dictionary<Guid, Vertice>();
            List<Vertice> vList=new List<Vertice>();
            foreach(Vertice v in this._Vertices.Values)
            {
                if(v.Tag.EntityType==entityType)
                {
                    vList.Add(v);
                    vs.Add(v.ID, v);
                }
            }
            List<Link> links=new List<Link>();
            NetworkWalker calc = new NetworkWalker(this.Vertices.Values.ToList(), this.Edges.Values.ToList());

            for(int i=0;i<vList.Count;i++)
            {
                Vertice v1 = vList[i];
                Dictionary<Vertice, Route> toRoutes = calc.GetShortestPathsFrom(v1);
                
                for (int k = i + 1; k < vList.Count; k++)
                {
                    Vertice v2 = vList[k];
                    if (!toRoutes.ContainsKey(v2))
                        continue;

                    Route shortcut = toRoutes[v2];
                    if (shortcut.Distance == double.PositiveInfinity || shortcut.Distance <= 0)
                        continue;
                    Dictionary<EntityType, List<Guid>> hops = new Dictionary<EntityType, List<Guid>>();
                    int hopCount = shortcut.Edges.Count - 1;
                    foreach(Edge e in shortcut.Edges)
                    {
                        if (e.VerticeID1 != v1.ID && e.VerticeID1 != v2.ID)
                        {
                            Vertice hop = this._Vertices[e.VerticeID1];
                            List<Guid> guids=new List<Guid>();
                            if (!hops.ContainsKey(hop.Tag.EntityType))
                            {
                                guids.Add(e.VerticeID1);
                                hops.Add(hop.Tag.EntityType, guids);
                            }
                            else
                            {
                                guids = hops[hop.Tag.EntityType];
                                if(!guids.Contains(e.VerticeID1))
                                    guids.Add(e.VerticeID1);
                                hops[hop.Tag.EntityType] = guids;
                            }
                        }
                        if (e.VerticeID2 != v1.ID && e.VerticeID2 != v2.ID)
                        {
                            List<Guid> guids=new List<Guid>();
                            Vertice hop = this._Vertices[e.VerticeID2];
                            if (!hops.ContainsKey(hop.Tag.EntityType))
                            {
                                guids.Add(e.VerticeID2);
                                hops.Add(hop.Tag.EntityType, guids);
                            }
                            else
                            {
                                guids = hops[hop.Tag.EntityType];
                                if(!guids.Contains(e.VerticeID2))
                                    guids.Add(e.VerticeID2);
                                hops[hop.Tag.EntityType] = guids;
                            }
                        }
                    }
                    bool exceedHopLimit = false;
                    if (hopCount > maxHop)
                        exceedHopLimit = true;
                    else
                    {
                        foreach (EntityType et in hops.Keys)
                        {
                            if (maxHopsByEntityTypes != null &&
                                maxHopsByEntityTypes.ContainsKey(et) && 
                                hops[et].Count>maxHopsByEntityTypes[et])
                            {
                                exceedHopLimit = true;
                                break;
                            }
                        }
                    }
                    if(exceedHopLimit)
                        continue;

                    Range<DateTime> timeRange=new Range<DateTime>();
                    DateTime dateFrom = TimeUtil.DbMaxTime;
                    DateTime dateTo = TimeUtil.DbMinTime;
                    foreach(Edge e in shortcut.Edges)
                    {
                        if(e.Tag.LinkTimeSpan.FromValue != TimeUtil.DbMinTime && 
                            e.Tag.LinkTimeSpan.FromValue < dateFrom)
                            dateFrom = e.Tag.LinkTimeSpan.FromValue;
                        if(e.Tag.LinkTimeSpan.ToValue != TimeUtil.DbMaxTime && 
                            e.Tag.LinkTimeSpan.ToValue > dateTo)
                            dateTo = e.Tag.LinkTimeSpan.ToValue;
                    }
                    if(dateFrom==TimeUtil.DbMaxTime)
                        dateFrom = TimeUtil.DbMinTime;
                    if(dateTo==TimeUtil.DbMinTime)
                        dateTo = TimeUtil.DbMaxTime;
                    timeRange=new Range<DateTime>(dateFrom, dateTo);

                    List<Route> alternateRoutes = calc.GetAllRoutesBetween(v1, v2, maxHop);
                    int aggregate = 1;
                    
                    if(alternateRoutes.Count>1)
                    {
                        aggregate = 0;
                        Dictionary<EntityType, List<Guid>> altHopsByEntityTypes =
                            new Dictionary<EntityType, List<Guid>>();
                        foreach(Route alterRoute in alternateRoutes)
                        {
                            if (alterRoute.Distance == shortcut.Distance)
                            {
                                if (aggregateByEntityTypes == null || aggregateByEntityTypes.Count == 0)
                                    aggregate++;
                                else
                                {
                                    foreach(Edge e in alterRoute.Edges)
                                    {
                                        if (e.VerticeID1 != v1.ID && e.VerticeID1 != v2.ID)
                                        {
                                            Vertice hop = this._Vertices[e.VerticeID1];
                                            if (aggregateByEntityTypes.Contains(hop.Tag.EntityType))
                                            {
                                                List<Guid> guids = new List<Guid>();
                                                if (!altHopsByEntityTypes.ContainsKey(hop.Tag.EntityType))
                                                {
                                                    guids.Add(e.VerticeID1);
                                                    altHopsByEntityTypes.Add(hop.Tag.EntityType, guids);
                                                }
                                                else
                                                {
                                                    guids = altHopsByEntityTypes[hop.Tag.EntityType];
                                                    if (!guids.Contains(e.VerticeID1))
                                                        guids.Add(e.VerticeID1);
                                                    altHopsByEntityTypes[hop.Tag.EntityType] = guids;
                                                }
                                            }
                                        }
                                        if (e.VerticeID2 != v1.ID && e.VerticeID2 != v2.ID)
                                        {
                                            List<Guid> guids = new List<Guid>();
                                            Vertice hop = this._Vertices[e.VerticeID2];
                                            if (aggregateByEntityTypes.Contains(hop.Tag.EntityType))
                                            {
                                                if (!altHopsByEntityTypes.ContainsKey(hop.Tag.EntityType))
                                                {
                                                    guids.Add(e.VerticeID2);
                                                    altHopsByEntityTypes.Add(hop.Tag.EntityType, guids);
                                                }
                                                else
                                                {
                                                    guids = altHopsByEntityTypes[hop.Tag.EntityType];
                                                    if (!guids.Contains(e.VerticeID2))
                                                        guids.Add(e.VerticeID2);
                                                    altHopsByEntityTypes[hop.Tag.EntityType] = guids;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        if(altHopsByEntityTypes.Count>0)
                        {
                            foreach(EntityType et in altHopsByEntityTypes.Keys)
                            {
                                aggregate += altHopsByEntityTypes[et].Count;
                            }
                        }
                        // shortcut.Distance = shortcut.Distance / aggregate;
                    }
                    Link lk = new Link(
                        v1.Tag.GetType(), v2.Tag.GetType(),
                        v1.Tag.PKValue(), v2.Tag.PKValue(),
                        shortcut.Distance, aggregate,
                        LinkType.Citation, false);
                    lk.LinkTimeSpan = timeRange;
                    links.Add(lk);
                }
            }
            this._Vertices = vs;
            this._Edges=new Dictionary<Guid, Edge>();
            foreach (Link link in links)
            {
                Guid fromVID = Guid.Empty;
                Guid toVID = Guid.Empty;
                EntityType fromType = EntityTypeUtil.GetEntityType(link.FromType);
                EntityType toType = EntityTypeUtil.GetEntityType(link.ToType);
                foreach (Vertice v in this._Vertices.Values)
                {
                    if (v.Tag.EntityType == fromType && link.FromKey == v.Tag.PKValue())
                        fromVID = v.ID;
                    if (v.Tag.EntityType == toType && link.ToKey == v.Tag.PKValue())
                        toVID = v.ID;
                }
                if (fromVID != Guid.Empty && toVID != Guid.Empty)
                {
                    if (link.Directional)
                    {
                        Arc arc = new Arc(Guid.NewGuid(), link.RelationType.ToString(),
                            link.Distance, fromVID, toVID, link);
                        this._Edges.Add(arc.ID, arc);
                    }
                    else
                    {
                        Edge e = new Edge(
                            Guid.NewGuid(), link.RelationType.ToString(), link.Distance, fromVID, toVID, link);
                        this._Edges.Add(e.ID, e);
                    }
                }
            }
        }
        #endregion

        public List<Vertice> GetHighlightedVertices(Vertice v)
        {
            Dictionary<Guid, Vertice> hightedVertices = new Dictionary<Guid, Vertice>();
            foreach(Edge e in this._Edges.Values)
            {
                if(e.VerticeID1==v.ID)
                {
                    if(!hightedVertices.ContainsKey(e.VerticeID2))
                    {
                        hightedVertices.Add(e.VerticeID2, this._Vertices[e.VerticeID2]);
                    }
                }
                if(e.VerticeID2==v.ID)
                {
                    if(!hightedVertices.ContainsKey(e.VerticeID2))
                    {
                        hightedVertices.Add(e.VerticeID1, this._Vertices[e.VerticeID1]);
                    }
                }
            }
            return hightedVertices.Values.ToList();
        }
    }

    
}
